/*
 * Copyright 2020 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#ifndef _FSL_PHY_H_
#define _FSL_PHY_H_

#include "fsl_mdio.h"

/*! @brief Defines the PHY registers. */
#define PHY_BASICCONTROL_REG        0x00U /*!< The PHY basic control register. */
#define PHY_BASICSTATUS_REG         0x01U /*!< The PHY basic status register. */
#define PHY_ID1_REG                 0x02U /*!< The PHY ID one register. */
#define PHY_ID2_REG                 0x03U /*!< The PHY ID two register. */
#define PHY_AUTONEG_ADVERTISE_REG   0x04U /*!< The PHY auto-negotiate advertise register. */
#define PHY_AUTONEG_LINKPARTNER_REG 0x05U /*!< The PHY auto negotiation link partner ability register. */
#define PHY_AUTONEG_EXPANSION_REG   0x06U /*!< The PHY auto negotiation expansion register. */
#define PHY_1000BASET_CONTROL_REG   0x09U /*!< The PHY 1000BASE-T control register. */
#define PHY_MMD_ACCESS_CONTROL_REG  0x0DU /*!< The PHY MMD access control register. */
#define PHY_MMD_ACCESS_DATA_REG     0x0EU /*!< The PHY MMD access data register. */

/*! @brief Defines the mask flag in basic control register. */
#define PHY_BCTL_DUPLEX_MASK          0x0100U /*!< The PHY duplex bit mask. */
#define PHY_BCTL_RESTART_AUTONEG_MASK 0x0200U /*!< The PHY restart auto negotiation mask. */
#define PHY_BCTL_AUTONEG_MASK         0x1000U /*!< The PHY auto negotiation bit mask. */
#define PHY_BCTL_SPEED0_MASK          0x2000U /*!< The PHY speed bit mask. */
#define PHY_BCTL_LOOP_MASK            0x4000U /*!< The PHY loop bit mask. */
#define PHY_BCTL_RESET_MASK           0x8000U /*!< The PHY reset bit mask. */
#define PHY_BCTL_SPEED_100M_MASK      0x2000U /*!< The PHY 100M speed mask. */
#define PHY_BCTL_SPEED1_MASK          0x40U   /*!< The PHY speed 1 mask.*/

/*! @brief Defines the mask flag in basic status register. */
#define PHY_BSTATUS_LINKSTATUS_MASK  0x0004U /*!< The PHY link status mask. */
#define PHY_BSTATUS_AUTONEGABLE_MASK 0x0008U /*!< The PHY auto-negotiation ability mask. */
#define PHY_BSTATUS_SPEEDUPLX_MASK   0x001CU /*!< The PHY speed and duplex mask. */
#define PHY_BSTATUS_AUTONEGCOMP_MASK 0x0020U /*!< The PHY auto-negotiation complete mask. */

/*! @brief Defines the mask flag in PHY auto-negotiation advertise register. */
#define PHY_100BaseT4_ABILITY_MASK    0x200U /*!< The PHY have the T4 ability. */
#define PHY_100BASETX_FULLDUPLEX_MASK 0x100U /*!< The PHY has the 100M full duplex ability.*/
#define PHY_100BASETX_HALFDUPLEX_MASK 0x080U /*!< The PHY has the 100M full duplex ability.*/
#define PHY_10BASETX_FULLDUPLEX_MASK  0x040U /*!< The PHY has the 10M full duplex ability.*/
#define PHY_10BASETX_HALFDUPLEX_MASK  0x020U /*!< The PHY has the 10M full duplex ability.*/

/*! @brief Defines the mask flag in the 1000BASE-T control register. */
#define PHY_1000BASET_FULLDUPLEX_MASK 0x200U /*!< The PHY has the 1000M full duplex ability.*/
#define PHY_1000BASET_HALFDUPLEX_MASK 0x100U /*!< The PHY has the 1000M half duplex ability.*/

/*******************************************************************************
 * Definitions
 ******************************************************************************/
typedef struct _phy_handle phy_handle_t;
/*! @brief Defines the PHY link speed. */
typedef enum _phy_speed
{
    kPHY_Speed10M = 0U, /*!< ENET PHY 10M speed. */
    kPHY_Speed100M,     /*!< ENET PHY 100M speed. */
    kPHY_Speed1000M     /*!< ENET PHY 1000M speed. */
} phy_speed_t;

/*! @brief Defines the PHY link duplex. */
typedef enum _phy_duplex
{
    kPHY_HalfDuplex = 0U, /*!< ENET PHY half duplex. */
    kPHY_FullDuplex       /*!< ENET PHY full duplex. */
} phy_duplex_t;

/*! @brief Defines the PHY loopback mode. */
typedef enum _phy_loop
{
    kPHY_LocalLoop = 0U, /*!< ENET PHY local/digital loopback. */
    kPHY_RemoteLoop,     /*!< ENET PHY remote loopback. */
    kPHY_ExternalLoop,   /*!< ENET PHY external loopback. */
} phy_loop_t;

/*! @brief Camera configuration. */
typedef struct _phy_config
{
    uint32_t phyAddr;    /*!< PHY address. */
    phy_speed_t speed;   /*!< PHY speed. */
    phy_duplex_t duplex; /*!< PHY duplex configuration. */
    bool autoNeg;        /*!< PHY auto-negociation. */
} phy_config_t;

/*! @brief PHY device operations. */
typedef struct _phy_operations
{
    status_t (*phyInit)(phy_handle_t *handle, const phy_config_t *config);
    status_t (*phyWrite)(phy_handle_t *handle, uint32_t phyReg, uint32_t data);
    status_t (*phyRead)(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr);
    status_t (*getLinkStatus)(phy_handle_t *handle, bool *status);
    status_t (*getLinkSpeedDuplex)(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex);
    status_t (*enableLoopback)(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable);
} phy_operations_t;

/*! @brief PHY device handle. */

struct _phy_handle
{
    uint32_t phyAddr;            /*!< PHY address. */
    mdio_handle_t *mdioHandle;   /*!< The MDIO handle used by the phy device, it is specified by device. */
    const phy_operations_t *ops; /*!< The device related operations. */
};

/*******************************************************************************
 * API
 ******************************************************************************/

#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @name PHY Driver
 * @{
 */

/*!
 * @brief Initializes PHY.
 *
 *  This function initialize PHY.
 *
 * @param handle       PHY device handle.
 * @param config       Pointer to structure of phy_config_t.
 * @retval kStatus_Success  PHY initialize success
 * @retval kStatus_PHY_SMIVisitTimeout  PHY SMI visit time out
 * @retval kStatus_PHY_AutoNegotiateFail  PHY auto negotiate fail
 */
static inline status_t PHY_Init(phy_handle_t *handle, const phy_config_t *config)
{
    return handle->ops->phyInit(handle, config);
}
/*!
 * @brief PHY Write function. This function write data over the SMI to
 * the specified PHY register. This function is called by all PHY interfaces.
 *
 * @param handle       PHY device handle.
 * @param phyReg  The PHY register.
 * @param data    The data written to the PHY register.
 * @retval kStatus_Success     PHY write success
 * @retval kStatus_PHY_SMIVisitTimeout  PHY SMI visit time out
 */
static inline status_t PHY_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data)
{
    return handle->ops->phyWrite(handle, phyReg, data);
}

/*!
 * @brief PHY Read function. This interface read data over the SMI from the
 * specified PHY register. This function is called by all PHY interfaces.
 *
 * @param handle       PHY device handle.
 * @param phyReg   The PHY register.
 * @param dataPtr  The address to store the data read from the PHY register.
 * @retval kStatus_Success  PHY read success
 * @retval kStatus_PHY_SMIVisitTimeout  PHY SMI visit time out
 */
static inline status_t PHY_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr)
{
    return handle->ops->phyRead(handle, phyReg, dataPtr);
}

/*!
 * @brief Gets the PHY link status.
 *
 * @param handle       PHY device handle.
 * @param status   The link up or down status of the PHY.
 *         - true the link is up.
 *         - false the link is down.
 * @retval kStatus_Success   PHY get link status success
 * @retval kStatus_PHY_SMIVisitTimeout  PHY SMI visit time out
 */
static inline status_t PHY_GetLinkStatus(phy_handle_t *handle, bool *status)
{
    return handle->ops->getLinkStatus(handle, status);
}

/*!
 * @brief Gets the PHY link speed and duplex.
 *
 * @param handle       PHY device handle.
 * @param speed    The address of PHY link speed.
 * @param duplex   The link duplex of PHY.
 * @retval kStatus_Success   PHY get link speed and duplex success
 * @retval kStatus_PHY_SMIVisitTimeout  PHY SMI visit time out
 */
static inline status_t PHY_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex)
{
    return handle->ops->getLinkSpeedDuplex(handle, speed, duplex);
}

/*!
 * @brief Enable PHY loopcback mode.
 *
 * @param handle   PHY device handle.
 * @param mode     The loopback mode to be enabled, please see "phy_loop_t".
 * All loopback modes should not be set together, when one loopback mode is set
 * another should be disabled.
 * @param speed    PHY speed for loopback mode.
 * @param enable   True to enable, false to disable.
 * @retval kStatus_Success   PHY get link speed and duplex success
 * @retval kStatus_PHY_SMIVisitTimeout  PHY SMI visit time out
 */
static inline status_t PHY_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable)
{
    return handle->ops->enableLoopback(handle, mode, speed, enable);
}

/* @} */

#if defined(__cplusplus)
}
#endif

/*! @}*/
#endif
